using System;
using System.Collections.Generic;
using System.Text;

using AgilePlannerDT.APWebservice;
using AgilePlannerDT.Data;
using AgilePlannerDT.Data.Impl;
using System.Windows.Media;
using AgilePlannerDT.Display;
using System.Web.Services.Protocols;
using System.Net;
using System.Diagnostics;
using AgilePlannerDT.Utils;

namespace AgilePlannerDT.Persister.Impl.Webservice
{
    public class AsyncWebServicePersister : AbstractAsyncPersister
    {
        #region Constants

        protected static readonly string CONNECTION_FAILED = "EX001: ";
        protected static readonly string COULD_NOT_LOAD_PROJECT = "EX002: ";
        protected static readonly string FORBIDDEN_OPERATION = "EX003: ";
        protected static readonly string GENERIC_EXCEPTION = "EX004: ";
        protected static readonly string INDEXCARD_NOT_FOUND = "EX005: ";
        protected static readonly string NOT_CONNECTED = "EX006: ";
        protected static readonly string PROJECT_NOT_FOUND = "EX007: ";

        public static readonly string DEFAULT_WEBSERVICE_URL = "http://bocksclone.cpsc.ucalgary.ca:8080/services/APWebservice?wsdl";

        #endregion

        #region Attributes

        protected APWebserviceService ws;
        protected string url;
        protected Project project;

        protected long callCounter = 0;

        #endregion

        #region Properties

        public override Project Project
        {
            get { return (Project)project.Clone(); }
            set { }
        }

        public string Url
        {
            get { return ws.Url; }
            set
            {
                if (ws.Url.Equals(value))
                {
                    return;
                }
                ws.Url = value;
            }
        }

        #endregion

        #region Constructors

        public AsyncWebServicePersister()
            : this(new Uri(DEFAULT_WEBSERVICE_URL))
        {
        }

        public AsyncWebServicePersister(Uri uri)
        {
            ws = new APWebserviceService();
            ws.Url = uri.ToString();
            ws.Timeout = 10000;

            //register event listeners for every asynchronous call to the web service
            ws.arrangeProjectCompleted += new arrangeProjectCompletedEventHandler(ws_arrangeProjectCompleted);

            ws.createBacklogCompleted += new createBacklogCompletedEventHandler(ws_createBacklogCompleted);
            ws.createIterationCompleted += new createIterationCompletedEventHandler(ws_createIterationCompleted);
            ws.createProjectCompleted += new createProjectCompletedEventHandler(ws_createProjectCompleted);
            ws.createStoryCardCompleted += new createStoryCardCompletedEventHandler(ws_createStoryCardCompleted);

            ws.deleteCardCompleted += new deleteCardCompletedEventHandler(ws_deleteCardCompleted);
            ws.deleteProjectCompleted += new deleteProjectCompletedEventHandler(ws_deleteProjectCompleted);

            ws.getProjectCompleted += new getProjectCompletedEventHandler(ws_getProjectCompleted);
            ws.getProjectNamesCompleted += new getProjectNamesCompletedEventHandler(ws_getProjectNamesCompleted);

            ws.loadCompleted += new loadCompletedEventHandler(ws_loadCompleted);

            ws.moveStoryCardToNewParentCompleted += new moveStoryCardToNewParentCompletedEventHandler(ws_moveStoryCardToNewParentCompleted);

            ws.undeleteCardCompleted += new undeleteCardCompletedEventHandler(ws_undeleteCardCompleted);
            ws.updateCardCompleted += new updateCardCompletedEventHandler(ws_updateCardCompleted);

        }

        #endregion

        #region AsynchronousPersister Members

        #region Yet unimplemented and with an undecided future

        public override bool IsConnected() { return false; }

        public override void Connect(string projectname) { }

        public override void Disconnect() { }

        public override void Load(string projectName, DateTime start, DateTime end) { }

        public override void UpdateProject(Project project) { }

        public override void Login(string userName, string password) { }

        public override void MouseMove(MouseMove move)
        {

        }

        #endregion

        #region Dot Voting

        public override void StartVoting()
        {
            throw new NotImplementedException();
        }

        public override void ClearVotes()
        {
            throw new NotImplementedException();
        }

        public override void VoteCard(VoteDot dot)
        {
            throw new NotImplementedException();
        }

        public override void StopVoting()
        {
            throw new NotImplementedException();
        }

        #endregion

       

        public override void Load(string projectName)
        {
            ws.loadAsync(projectName, callCounter++);
        }

        public override void GetProjectNames()
        {
            ws.getProjectNamesAsync(callCounter++);
        }

        public override void CreateBacklog(int width, int height, int locationX, int locationY)
        {
            ws.createBacklogAsync(width, height, locationX, locationY, callCounter++);
        }

        public override void CreateIteration(string name, string description, int width, int height, int locationX, int locationY, float availableEffort, DateTime startDate, DateTime endDate)
        {
            ws.createIterationAsync(name, description, width, height, locationX, locationY, availableEffort, startDate, endDate, callCounter++);
        }

        public override void CreateStoryCard(string name, string description, int width, int height, int locationX, int locationY, long parentid, float bestCaseEstimate, float mostlikelyEstimate, float worstCaseEstimate, float actualEffort, string status, string color, string cardOwner, float rotationAngle)
        {
            ws.createStoryCardAsync(name, description, width, height, locationX, locationY, parentid, bestCaseEstimate, mostlikelyEstimate, worstCaseEstimate, actualEffort, status, color, cardOwner, rotationAngle, callCounter++);
        }

        public override void CreateProject(string projectName)
        {
            ws.createProjectAsync(projectName, callCounter++);
        }

        public override void DeleteBacklog(long id)
        {
            ws.deleteCardAsync(id, callCounter++);
        }

        public override void DeleteIteration(long id)
        {
            ws.deleteCardAsync(id, callCounter++);
        }

        public override void DeleteStoryCard(long id)
        {
            ws.deleteCardAsync(id, callCounter++);
        }

        public override void DeleteProject(String projectName)
        {
            ws.deleteProjectAsync(projectName, callCounter++);
        }

        public override void UndeleteIteration(Iteration iteration)
        {
            ws.undeleteCardAsync(ToWsIteration(iteration), callCounter++);
        }

        public override void UndeleteStoryCard(StoryCard storycard)
        {
            ws.undeleteCardAsync(ToWsStoryCard(storycard), callCounter++);
        }

        public override void MoveStoryCardToNewParent(StoryCard sc, long newparentid, int locationX, int locationY, float rotationAngle)
        {
            ws.moveStoryCardToNewParentAsync(sc.ID, sc.Parent, newparentid, locationX, locationY, rotationAngle, callCounter++);
        }

        public override void UpdateIteration(Iteration iteration)
        {
            ws.updateCardAsync(ToWsIteration(iteration), callCounter++);
        }

        public override void UpdateStoryCard(StoryCard storycard)
        {
            ws.updateCardAsync(ToWsStoryCard(storycard), callCounter++);
        }

        public override void UpdateLegend(Legend legend)
        {
            ws.updateLegendAsync(ToWsLegend(legend), callCounter++);
        }

        public override void UpdateBacklog(Backlog backlog)
        {
            ws.updateCardAsync(ToWsBacklog(backlog), callCounter++);
        }

        public override void ArrangeProject(Project project)
        {
            ws.arrangeProjectAsync(ToWsProject(project), callCounter++);
        }

        #endregion

        #region Object conversions

        public wsLegend ToWsLegend(Legend legend)
        {
            return (wsLegend)new PlanningObjectConverter().ConvertTo(legend, typeof(wsLegend));
        }

        public wsStoryCard ToWsStoryCard(StoryCard storycard)
        {
            return (wsStoryCard)new PlanningObjectConverter().ConvertTo(storycard, typeof(wsStoryCard));
        }

        public wsIteration ToWsIteration(Iteration iteration)
        {
            return (wsIteration)new PlanningObjectConverter().ConvertTo(iteration, typeof(wsIteration));
        }

        public wsProject ToWsProject(Project project)
        {
            return (wsProject)new PlanningObjectConverter().ConvertTo(project, typeof(wsProject));
        }

        public wsBacklog ToWsBacklog(Backlog backlog)
        {
            return (wsBacklog)new PlanningObjectConverter().ConvertTo(backlog, typeof(wsBacklog));
        }

        public StoryCard ToStoryCard(wsStoryCard storycard)
        {
            return (StoryCard)new PlanningObjectConverter().ConvertFrom(storycard);
        }

        public Iteration ToIteration(wsIteration iteration)
        {
            return (Iteration)new PlanningObjectConverter().ConvertFrom(iteration);
        }

        public Legend ToLegend(wsLegend legend)
        {
            return (Legend)new PlanningObjectConverter().ConvertFrom(legend);
        }

        public Project ToProject(wsProject project)
        {
            return (Project)new PlanningObjectConverter().ConvertFrom(project);
        }

        /// <summary>
        /// Converts wsBacklog to Backlog
        /// </summary>
        /// <param name="backlog">wsBacklog to convert</param>
        /// <returns>Converted Backlog</returns>
        public Backlog ToBacklog(wsBacklog backlog)
        {
            return (Backlog)new PlanningObjectConverter().ConvertFrom(backlog);
        }

        private String CheckStringForNull(String str)
        {
            if (str == null)
                return "";
            else
                return str;
        }

        private byte[] CheckByteArrayForNull(byte[] ba)
        {
            if (ba == null)
                return new byte[0];
            else
                return ba;
        }

        #endregion

        #region Callbacks

        void ws_createBacklogCompleted(object sender, createBacklogCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Backlog backlog = ToBacklog(e.Result);
                CreatedBacklog((Backlog)project.CreateBacklog(backlog).Clone());
            }
            else
            {
                if (e.Error.Message.StartsWith(FORBIDDEN_OPERATION))
                {
                    AsynchronousException(new ForbiddenOperationException(e.Error.Message));
                }
                else
                {
                    AsynchronousException(e.Error);
                }
            }
        }

        void ws_createIterationCompleted(object sender, createIterationCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Iteration iteration = ToIteration(e.Result);
                CreatedIteration((Iteration)project.CreateIteration(iteration).Clone());
            }
            else
            {
                AsynchronousException(e.Error);
            }
        }

        void ws_createProjectCompleted(object sender, createProjectCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Project prj = ToProject(e.Result);
                this.project = prj;
                CreatedProject((Project)project.Clone());
            }
            else
            {
                AsynchronousException(e.Error);
            }
        }

        void ws_createStoryCardCompleted(object sender, createStoryCardCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                StoryCard sc = ToStoryCard(e.Result);
                CreatedStoryCard((StoryCard)project.CreateStoryCard(sc).Clone());
            }
            else
            {
                if (e.Error.Message.StartsWith(INDEXCARD_NOT_FOUND))
                {
                    AsynchronousException(new IndexCardNotFoundException(e.Error.Message));
                }
                else
                {
                    AsynchronousException(e.Error);
                }
            }
        }

        void ws_deleteCardCompleted(object sender, deleteCardCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (e.Result is wsStoryCard)
                {
                    DeletedStoryCard((StoryCard)project.DeleteCard(e.Result.id).Clone());
                }
                else if (e.Result is wsIteration)
                {
                    DeletedIteration((Iteration)project.DeleteCard(e.Result.id).Clone());
                }
                else if (e.Result is wsBacklog)
                {
                    DeletedBacklog((Backlog)project.DeleteCard(e.Result.id).Clone());
                }
            }
            else
            {
                if (e.Error.Message.StartsWith(FORBIDDEN_OPERATION))
                    AsynchronousException(new ForbiddenOperationException(e.Error.Message));
                else if (e.Error.Message.StartsWith(INDEXCARD_NOT_FOUND))
                    AsynchronousException(new IndexCardNotFoundException(e.Error.Message));
                else
                    AsynchronousException(e.Error);
            }
        }

        void ws_deleteProjectCompleted(object sender, deleteProjectCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                DeletedProject(e.Result);
            }
            else
            {
                if (e.Error.Message.StartsWith(PROJECT_NOT_FOUND))
                    AsynchronousException(new ProjectNotFoundException(e.Error.Message));
                else
                    AsynchronousException(e.Error);
            }
        }

        void ws_updateCardCompleted(object sender, updateCardCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (e.Result is wsStoryCard)
                {
                    StoryCard sc = ToStoryCard((wsStoryCard)e.Result);
                    UpdatedStoryCard((StoryCard)project.UpdateCard(sc).Clone());
                }
                else if (e.Result is wsIteration)
                {
                    Iteration iter = ToIteration((wsIteration)e.Result);
                    UpdatedIteration((Iteration)project.UpdateCard(iter).Clone());
                }
                else if (e.Result is wsBacklog)
                {
                    Backlog back = ToBacklog((wsBacklog)e.Result);
                    UpdatedBacklog((Backlog)project.UpdateCard(back).Clone());
                }

            }
            else
            {
                if (e.Error.Message.StartsWith(INDEXCARD_NOT_FOUND))
                    AsynchronousException(new IndexCardNotFoundException(e.Error.Message));
                else
                    AsynchronousException(e.Error);
            }

        }

        void ws_undeleteCardCompleted(object sender, undeleteCardCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (e.Result is wsStoryCard)
                {
                    StoryCard sc = ToStoryCard((wsStoryCard)e.Result);
                    UndeletedStoryCard((StoryCard)project.UndeleteCard(sc).Clone());
                }
                else if (e.Result is wsIteration)
                {
                    Iteration iter = ToIteration((wsIteration)e.Result);
                    UndeletedIteration((Iteration)project.UndeleteCard(iter).Clone());
                }
                else if (e.Result is wsBacklog)
                {
                    Backlog back = ToBacklog((wsBacklog)e.Result);
                    UndeletedBacklog((Backlog)project.UndeleteCard(back).Clone());
                }

            }
            else
            {
                if (e.Error.Message.StartsWith(INDEXCARD_NOT_FOUND))
                    AsynchronousException(new IndexCardNotFoundException(e.Error.Message));
                else if (e.Error.Message.StartsWith(FORBIDDEN_OPERATION))
                    AsynchronousException(new ForbiddenOperationException(e.Error.Message));
                else
                    AsynchronousException(e.Error);
            }
        }

        void ws_moveStoryCardToNewParentCompleted(object sender, moveStoryCardToNewParentCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                StoryCard sc = ToStoryCard(e.Result);
                MovedStoryCardToNewParent((StoryCard)project.MoveStoryCardToNewParent(sc.ID, project.FindCard(project.FindCard(sc.ID).Parent).ID, sc.Parent, sc.LocationX, sc.LocationY, sc.RotationAngle).Clone());
            }
            else
            {
                if (e.Error.Message.StartsWith(INDEXCARD_NOT_FOUND))
                {
                    AsynchronousException(new IndexCardNotFoundException(e.Error.Message));
                }
                else
                {
                    AsynchronousException(e.Error);
                }
            }
        }

        void ws_loadCompleted(object sender, loadCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Project prj = ToProject(e.Result);
                this.project = prj;

                LoadedProject((Project)project.Clone());
            }
            else
            {
                if (e.Error.Message.StartsWith(COULD_NOT_LOAD_PROJECT))
                    AsynchronousException(new CouldNotLoadProjectException(e.Error.Message));
                else
                    AsynchronousException(e.Error);
            }
        }

        void ws_getProjectNamesCompleted(object sender, getProjectNamesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                GotProjectNames(new List<String>(e.Result));
            }
            else
            {
                AsynchronousException(e.Error);
            }
        }

        void ws_getProjectCompleted(object sender, getProjectCompletedEventArgs e)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        void ws_getIterationNamesCompleted(object sender, getIterationNamesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                //GotIterationNames(e.Result);
            }
            else
            {
                AsynchronousException(e.Error);
            }
        }

        void ws_arrangeProjectCompleted(object sender, arrangeProjectCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Project prj = ToProject(e.Result);
                this.project = prj;
                ArrangedProject((Project)project.Clone());
            }
            else
            {
                AsynchronousException(e.Error);
            }
        }

        #endregion

        public override void LiveUpdate(LiveUpdate update)
        {
            throw new NotImplementedException();
        }

        public override void MinimizeCard(long ID)
        {
            
        }

        public override void SynchWithJazz(int MessageType, Dictionary<string, string> data)
        {
            
        }

        public override void AddTeamMember(TeamMember member)
        {
            throw new NotImplementedException();
        }

        public override void RemoveTeamMember(TeamMember member)
        {
            throw new NotImplementedException();
        }
    }
}